package com.wmx.service.impl;

import com.wmx.annotation.RedisLock;
import com.wmx.entity.TV;
import com.wmx.repository.TVRepository;
import com.wmx.service.TVService;
import com.wmx.service.TVServiceExt;
import org.springframework.aop.framework.AopContext;
import org.springframework.data.domain.Sort;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.ObjectUtils;

import javax.annotation.Resource;
import java.util.List;
import java.util.concurrent.CountDownLatch;

/**
 * 电视业务层接口实现
 *
 * @author wangMaoXiong
 * @version 1.0
 * @date 2022/4/9 8:33
 */
@Service
public class TVServiceImpl implements TVService {

    @Resource
    private TVRepository tvRepository;

    @Resource
    private TVServiceExt tvServiceExt;

    @Override
    public List<TV> findAll() {
        //查询所有数据，并以主键 tvId 倒序排序
        List<TV> repositoryAll = tvRepository.findAll(Sort.by(Sort.Direction.DESC, "tvId"));

        if (!ObjectUtils.isEmpty(repositoryAll)) {
            TVServiceImpl tvServiceProxy = (TVServiceImpl) AopContext.currentProxy();
            // tvServiceProxy.deleteById(repositoryAll.get(0).getTvId());
        }
        return repositoryAll;
    }

    /**
     * org.springframework.transaction.annotation.Transactional：事务管理
     * Transactional 注解添加到哪个方法上，哪个方法就会进行事务管理.默认传播特性为 REQUIRED
     *
     * @param tv
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    @RedisLock(retryCount = 3, lockTime = 30, lockFiled = -1, desc = "redis分布式锁")
    public void save(TV tv) {
        //调用其它类中的方法
        tvServiceExt.deleteByIdExt(10);

        //故意制造一个数组下标越界异常，典型的运行时异常
        // System.out.println("123".split(",")[1]);
        tv.setTvName(tv.getTvName() + "_xxx");
        tvRepository.save(tv);
    }

    /**
     * 多线程测试1
     * 如下所示子线程中发生异常后：
     * 1、因为是异步的，主线程启动子线程后，主线程会自动继续先后执行。
     * 2、子线程里面并没有受外部方法的事务控制，发生异常数据也不会回滚，更不会影响主线程，因为主线程已经走了。
     *
     * @param tv
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void save2(TV tv) {
        System.out.println("............start...");
        CountDownLatch countDownLatch = new CountDownLatch(1);
        Runnable b = new Runnable() {
            @Override
            public void run() {
                try {
                    tv.setTvName(tv.getTvName() + "_save2");
                    tvRepository.save(tv);
                    // 故意制造一个数组下标越界异常，典型的运行时异常
                    // Exception in thread "Thread-20" java.lang.ArrayIndexOutOfBoundsException: 1
                    System.out.println("123".split(",")[1]);
                } finally {
                    countDownLatch.countDown();
                }
            }
        };
        new Thread(b).start();
        try {
            countDownLatch.await();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("............end...");
    }

    /**
     * 多线程测试2
     * 1、子线程中调用其它类中的方法，而目标方法中有事务注解时，则目标方法受事务控制。
     * 2、子线程中直接调用本类中的方法，即使目标方法有事务注解时，目标方法也不受事务控制。
     * 3、主线程、子线程获取到的数据库连接不一样，从而是两个不同的事务。
     *
     * @param id
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void deleteById2(int id) {
        System.out.println("............start...");
        CountDownLatch countDownLatch = new CountDownLatch(1);
        Runnable b = new Runnable() {
            @Override
            public void run() {
                try {
                    // 线程中调用其它类中的方法，而目标方法中有事务注解时，则目标方法受事务控制。
                    // tvServiceExt.deleteByIdExt(id);

                    // 子线程中直接调用本类中的方法，即使目标方法有事务注解时，目标方法也不受事务控制.
                    deleteById(id);
                } finally {
                    countDownLatch.countDown();
                }
            }
        };
        new Thread(b).start();
        try {
            countDownLatch.await();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("............end...");
    }

    @Override
    // @Transactional(rollbackFor = Exception.class)
    public void deleteById(int id) {
        tvRepository.deleteById(id);
        // System.out.println("123".split(",")[1]);
    }

}
